
#include <iostream>

#include "QmitkKillBone.h"
#include <QAction>

#include <QInputDialog>
#include <QMessageBox>
#include <QCheckBox>
#include <QSpinBox>
#include <QSlider>
#include <vtkRenderWindow.h>
#include <mitkContour.h>



#include <mitkRenderingManager.h>
#include <mitkProperties.h>
#include <mitkGlobalInteraction.h>
#include "mitkITKImageImport.h"
#include "mitkUndoController.h"
#include "mitkIDataStorageService.h"
#include "mitkNodePredicateDataType.h"
#include "mitkImageAccessByItk.h"

#include <itkCommand.h>

#include "WxContourMapperOverlay.h"
#include "WxRenderToContourCutter.h"
#include <vtkSphereSource.h>

// ITK
#include <itkConnectedThresholdImageFilter.h>
#include <itkMaskNegatedImageFilter.h>
//to be moved to mitkInteractionConst.h by StateMachineEditor
const mitk::OperationType QmitkKillBone::OP_EXCHANGE = 717;

// constructors for operation classes
QmitkKillBone::opExchangeNodes::opExchangeNodes( mitk::OperationType type, mitk::DataNode* node, mitk::BaseData* oldData, mitk::BaseData* newData )
:mitk::Operation(type),m_Node(node),m_OldData(oldData),m_NewData(newData), m_NodeDeletedObserverTag(0), m_OldDataDeletedObserverTag(0),
m_NewDataDeletedObserverTag(0)
{
  // listen to the node the image is hold
  itk::MemberCommand<opExchangeNodes>::Pointer nodeDeletedCommand = itk::MemberCommand<opExchangeNodes>::New();
  nodeDeletedCommand->SetCallbackFunction(this, &opExchangeNodes::NodeDeleted);

  m_NodeDeletedObserverTag = m_Node->AddObserver(itk::DeleteEvent(), nodeDeletedCommand);
  m_OldDataDeletedObserverTag = m_OldData->AddObserver(itk::DeleteEvent(), nodeDeletedCommand);
  m_NewDataDeletedObserverTag = m_NewData->AddObserver(itk::DeleteEvent(), nodeDeletedCommand);
}

// destructor for operation class
QmitkKillBone::opExchangeNodes::~opExchangeNodes()
{
  if (m_Node != NULL)
  {
    m_Node->RemoveObserver(m_NodeDeletedObserverTag);
    m_Node=NULL;
  }

  if (m_OldData.IsNotNull())
  {
    m_OldData->RemoveObserver(m_OldDataDeletedObserverTag);
    m_OldData=NULL;
  }

  if (m_NewData.IsNotNull())
  {
    m_NewData->RemoveObserver(m_NewDataDeletedObserverTag);
    m_NewData=NULL;
  }
}

void QmitkKillBone::opExchangeNodes::NodeDeleted(const itk::Object * /*caller*/, const itk::EventObject &/*event*/)
{
  m_Node = NULL;
  m_OldData = NULL;
  m_NewData = NULL;
}

QmitkKillBone::QmitkKillBone(QObject *parent)
: QObject(parent),
m_Controls(NULL)
{
}


QmitkKillBone::~QmitkKillBone()
{
  //delete smart pointer objects

}

void QmitkKillBone::CreateQtPartControl(QWidget* parent)
{
  if (!m_Controls)
  {
    // build ui elements
    m_Controls = new Ui::QmitkKillBoneControls;
    m_Controls->setupUi(parent);

    // setup ui elements
    m_Controls->groupInfo->hide();
    m_Controls->m_SurroundingSlider->show();
    m_Controls->m_SurroundingSpin->show();
    m_Controls->m_TLGrayvalue->show();


    // create ui element connections
    this->CreateConnections();

    m_Controls->cmbImage->SetDataStorage(this->GetDefaultDataStorage());
    m_Controls->cmbImage->SetPredicate(mitk::NodePredicateDataType::New("Image"));
  }

}

void QmitkKillBone::CreateConnections()
{
  if ( m_Controls )
  {
    connect( m_Controls->btnKillBone, SIGNAL(clicked()), this, SLOT(KillBone()));
    connect( m_Controls->m_StopOpacitySlider,SIGNAL(valueChanged(int)),this,SLOT(OnStopOpacityChanged(int)));
    connect( m_Controls->cmbImage, SIGNAL(OnSelectionChanged(const mitk::DataNode*)), this, SLOT(OnImageSelectionChanged(const mitk::DataNode*)) );
  }
}

void QmitkKillBone::Activated()
{
    QmitkFunctionality::Activated();  // just call the inherited function
    m_ImageNode = this->selectedImage();
    if (m_ImageNode.IsNull()) return;
    m_ImageToCrop = dynamic_cast<mitk::Image*>(m_ImageNode->GetData());
    // test, if image is selected
    if (m_ImageToCrop.IsNull()) return;

    vtkSphereSource* sphs=vtkSphereSource::New();
    sphs->SetRadius(5);
    sphs->SetPhiResolution(20);
    sphs->SetThetaResolution(20);
    sphs->Update();
    mitk::Surface::Pointer crossBallSurface=mitk::Surface::New();
    crossBallSurface->SetVtkPolyData(sphs->GetOutput());


    mitk::DataNode::Pointer crossBallNode = mitk::DataNode::New();
    crossBallNode->SetData(crossBallSurface);
    crossBallNode->SetName("Volume3DPickBall");
    crossBallNode->SetColor(0.8,0.1,0.1);
    crossBallNode->SetProperty("layer",mitk::IntProperty::New(1000));
    crossBallNode->SetVisibility(false);
    // crossBallNode->SetMapper(mitk::BaseRenderer::Standard2D,mitk::WxDoNothingMapper::New());
    this->GetDataStorage()->Add(crossBallNode);
    sphs->Delete();
    this->m_Picker=mitk::WxVolumeRenderingPicker::New("PressMoveRelease");
    this->m_Picker->SetImageNode(m_ImageNode);
    this->m_Picker->SetCrossBallNode(crossBallNode);
    mitk::GlobalInteraction::GetInstance()->AddListener(m_Picker);

}


void QmitkKillBone::Deactivated()
{
    mitk::DataNode* node=this->GetDataStorage()->GetNamedNode("Volume3DPickBall");
    if(node)  this->GetDataStorage()->Remove(node);
    if(m_Picker.IsNotNull())mitk::GlobalInteraction::GetInstance()->RemoveListener(m_Picker);
    QmitkFunctionality::Deactivated(); // just call the inherited function
    mitk::RenderingManager::GetInstance()->RequestUpdateAll();

}

/*! When called with an opExchangeNodes, it changes the content of a node from one data set to another
*/
void QmitkKillBone::ExecuteOperation (mitk::Operation *operation)
{
  if (!operation) return;

  switch (operation->GetOperationType())
  {
  case OP_EXCHANGE:
    {
      //RemoveBoundingObjectFromNode();
      opExchangeNodes* op = static_cast<opExchangeNodes*>(operation);
      op->GetNode()->SetData(op->GetNewData());
      mitk::RenderingManager::GetInstance()->InitializeViews();
      mitk::RenderingManager::GetInstance()->RequestUpdateAll();
      break;
    }
  default:;
  }

}

void QmitkKillBone::OnImageSelectionChanged(const mitk::DataNode* node)
{
    if(node!=m_ImageNode)
    {
        mitk::DataNode* node=this->GetDataStorage()->GetNamedNode("Volume3DPickBall");
        if(node) node->SetVisibility(false);
    }
    m_ImageNode = this->selectedImage();
    if(m_Picker.IsNotNull())
    {

       this->m_Picker->SetImageNode(m_ImageNode);
   }
}

const mitk::DataNode::Pointer QmitkKillBone::selectedImage()
{
  return m_Controls->cmbImage->GetSelectedNode();
}




void QmitkKillBone::StdMultiWidgetAvailable( QmitkStdMultiWidget&  stdMultiWidget )
{
  m_MultiWidget = &stdMultiWidget;
}

void QmitkKillBone::StdMultiWidgetNotAvailable()
{
  m_MultiWidget = NULL;
}

void QmitkKillBone::NodeRemoved(const mitk::DataNode* node)
{
//    if(node==NULL)
//    {
//        mitk::DataNode* ballnode=this->GetDataStorage()->GetNamedNode("Volume3DPickBall");
//        if(ballnode)  this->GetDataStorage()->Remove(ballnode);
//        if(m_Picker.IsNotNull())mitk::GlobalInteraction::GetInstance()->RemoveListener(m_Picker);
//    }
}

void QmitkKillBone::KillBone()
{
    std::vector<mitk::DataNode*> nodes = this->GetDataManagerSelection();
    if (nodes.empty()) return;

    mitk::DataNode* node = nodes.front();

    if (!node)
      return; // here we have a valid mitk::DataNode

    // a node itself is not very useful, we need its data item (the image)
    mitk::BaseData* data = node->GetData();
    if (data)
    {
      // test if this data item is an image or not (could also be a surface or something totally different)
      //  MITK_INFO<<sizeof(short);
      mitk::Image* image = dynamic_cast<mitk::Image*>( data );
      if (image)
      {
           const mitk::PixelType& pixelType = image->GetPixelType();
           if(pixelType== mitk::PixelType(typeid(short)))
           {
               assert(image->GetDimension()>=2 && image->GetDimension()<=3);
               if(image->GetDimension()==2)
               {
                   _accessByItk_2(image, ItkImageProcessing, short, 2, image->GetGeometry(), node)
               }
               else
               {
                   _accessByItk_2(image, ItkImageProcessing, short, 3, image->GetGeometry(), node)
               }


           }
      }
    }
}
template < typename TPixel, unsigned int VImageDimension >
void QmitkKillBone::ItkImageProcessing( itk::Image< TPixel, VImageDimension >* itkImage, mitk::Geometry3D* imageGeometry, mitk::DataNode* parent )
{
    typedef itk::Image< TPixel, VImageDimension > InputImageType;
    typedef typename InputImageType::IndexType    IndexType;

    // instantiate an ITK region growing filter, set its parameters
    typedef itk::ConnectedThresholdImageFilter<InputImageType, InputImageType> RegionGrowingFilterType;
    typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New();
    regionGrower->SetInput( itkImage ); // don't forget this

    // determine a thresholding interval

    TPixel min( std::numeric_limits<TPixel>::max() );
    TPixel max( std::numeric_limits<TPixel>::min() );
    mitk::WxVolumeRenderingPicker* picker=m_Picker;
    mitk::DataNode*node= picker->GetCrossBallNode();
    if(node)
    {

        mitk::Point3D p3d=node->GetData()->GetGeometry()->GetOrigin();
        // first test if this point is inside the image at all
        if ( imageGeometry->IsInside( p3d) )
        {
            IndexType seedIndex;
            imageGeometry->WorldToIndex( p3d, seedIndex);
            regionGrower->AddSeed(seedIndex);
        }
    }

    min = 250;
    max = 1500;

    // set thresholds and execute filter
    regionGrower->SetLower( this->m_Controls-> m_SurroundingSliderMin->value() );
    regionGrower->SetUpper(this->m_Controls-> m_SurroundingSliderMax->value() );
    regionGrower->Update();

    typedef itk::MaskNegatedImageFilter<InputImageType, InputImageType> MaskFilterType;
    typename MaskFilterType::Pointer maskFilter = MaskFilterType::New();
    maskFilter->SetInput1(itkImage);
    maskFilter->SetInput2(regionGrower->GetOutput() );
    maskFilter->SetOutsideValue(m_Controls->m_SurroundingSpin->value());
    maskFilter->Update();

    mitk::Image::Pointer resultImage = mitk::ImportItkImage( maskFilter->GetOutput() );
    mitk::DataNode::Pointer newNode = mitk::DataNode::New();
    newNode->SetData( resultImage );

    {
        opExchangeNodes*  doOp   = new opExchangeNodes(OP_EXCHANGE, m_ImageNode.GetPointer(),
                                                       m_ImageNode->GetData(),
                                                       resultImage);
        opExchangeNodes* undoOp  = new opExchangeNodes(OP_EXCHANGE, m_ImageNode.GetPointer(),
                                                       resultImage,
                                                       m_ImageNode->GetData());

        mitk::UndoController::GetCurrentUndoModel()->SetOperationEvent(
                new mitk::OperationEvent(this, doOp, undoOp, "Crop image") ); // tell the undo controller about the action
        ExecuteOperation(doOp); // execute action
    }
    mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}

void QmitkKillBone::OnStopOpacityChanged(int v)
{
    if(m_Picker.IsNotNull())
    {
       this->m_Picker->SetMaxOpacity(v/100.0);
    }
}
